package com.qiniuyun.backai.service;

import cn.hutool.core.lang.UUID;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.zip.GZIPOutputStream;
import java.util.concurrent.CompletableFuture;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.function.Consumer;

@Service
public class QiniuAsrService {
     
    private static final String QINIU_ASR_URL = "wss://openai.qiniu.com/v1/voice/asr";
    private static final String OPENAI_KEY = "sk-55fc91be04299a381adb27d457d2b271ca30e5712d86e31c370859ff26162414"; // 需要替换为实际的token
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    private WebSocketClient wsClient;
    private int seq = 1;
    private CompletableFuture<String> resultFuture;
    private Consumer<String> resultCallback;
    
    /**
     * 生成协议头
     */
    private byte[] generateHeader(int messageType, int flags, int serial, int compress) {
        byte[] header = new byte[4];
        header[0] = (byte) ((1 << 4) | 1);
        header[1] = (byte) ((messageType << 4) | flags);
        header[2] = (byte) ((serial << 4) | compress);
        header[3] = 0;
        return header;
    }
    
    /**
     * 生成序列号部分
     */
    private byte[] generateBeforePayload(int sequence) {
        ByteBuffer buffer = ByteBuffer.allocate(4);
        buffer.putInt(sequence);
        return buffer.array();
    }
    
    /**
     * GZIP压缩
     */
    private byte[] gzipCompress(byte[] data) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try (GZIPOutputStream gzipOut = new GZIPOutputStream(baos)) {
            gzipOut.write(data);
        }
        return baos.toByteArray();
    }
    
    /**
     * 发送ASR配置包
     */
    private void sendConfig() throws Exception {
        Map<String, Object> req = new HashMap<>();
        
        // 用户信息
        Map<String, String> user = new HashMap<>();
        user.put("uid", UUID.randomUUID().toString());
        req.put("user", user);
        
        // 音频配置
        Map<String, Object> audio = new HashMap<>();
        audio.put("format", "pcm");
        audio.put("sample_rate", 16000);
        audio.put("bits", 16);
        audio.put("channel", 1);
        audio.put("codec", "raw");
        req.put("audio", audio);
        
        // 请求配置
        Map<String, Object> request = new HashMap<>();
        request.put("model_name", "asr");
        request.put("enable_punc", true);
        req.put("request", request);
        
        String jsonStr = objectMapper.writeValueAsString(req);
        byte[] payload = gzipCompress(jsonStr.getBytes("UTF-8"));
        
        // 构建消息
        byte[] header = generateHeader(1, 1, 1, 1);
        byte[] seqBytes = generateBeforePayload(seq);
        byte[] lengthBytes = ByteBuffer.allocate(4).putInt(payload.length).array();
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(header);
        baos.write(seqBytes);
        baos.write(lengthBytes);
        baos.write(payload);
        
        wsClient.send(baos.toByteArray());
    }
    
    /**
     * 发送音频数据
     */
    public void sendAudioChunk(byte[] audioData) throws Exception {
        if (wsClient == null || !wsClient.isOpen()) {
            throw new IllegalStateException("WebSocket连接未建立");
        }
        
        seq++;
        byte[] compressed = gzipCompress(audioData);
        
        byte[] header = generateHeader(2, 1, 1, 1);
        byte[] seqBytes = generateBeforePayload(seq);
        byte[] lengthBytes = ByteBuffer.allocate(4).putInt(compressed.length).array();
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        baos.write(header);
        baos.write(seqBytes);
        baos.write(lengthBytes);
        baos.write(compressed);
        
        wsClient.send(baos.toByteArray());
    }
    
    /**
     * 解析服务端返回的文本内容
     */
    private String parseTextFromResponse(ByteBuffer data) {
        try {
            byte[] bytes = data.array();
            if (bytes.length < 4) return "";
            
            int headerSize = bytes[0] & 0x0f;
            int messageType = (bytes[1] >> 4) & 0x0f;
            int messageTypeSpecificFlags = bytes[1] & 0x0f;
            int serializationMethod = (bytes[2] >> 4) & 0x0f;
            int messageCompression = bytes[2] & 0x0f;
            
            byte[] payload = new byte[bytes.length - headerSize * 4];
            System.arraycopy(bytes, headerSize * 4, payload, 0, payload.length);
            
            if ((messageTypeSpecificFlags & 0x01) != 0 && payload.length >= 4) {
                byte[] newPayload = new byte[payload.length - 4];
                System.arraycopy(payload, 4, newPayload, 0, newPayload.length);
                payload = newPayload;
            }
            
            if (messageType == 0b1001 && payload.length >= 4) {
                int payloadSize = ByteBuffer.wrap(payload, 0, 4).getInt();
                if (payload.length >= 4 + payloadSize) {
                    byte[] newPayload = new byte[payloadSize];
                    System.arraycopy(payload, 4, newPayload, 0, payloadSize);
                    payload = newPayload;
                }
            }
            
            if (messageCompression == 0b0001) {
                // 这里需要实现GZIP解压缩，简化处理
                upzip(payload);
                // payload = gzipDecompress(payload);
            }
            
            String result = new String(payload, "UTF-8");
            
            if (serializationMethod == 0b0001) {
                Map<String, Object> obj = objectMapper.readValue(result, Map.class);
                if (obj.containsKey("result")) {
                    Map<String, Object> resultObj = (Map<String, Object>) obj.get("result");
                    if (resultObj.containsKey("text")) {
                        return (String) resultObj.get("text");
                    }
                }
                if (obj.containsKey("payload_msg")) {
                    Map<String, Object> payloadMsg = (Map<String, Object>) obj.get("payload_msg");
                    if (payloadMsg.containsKey("result")) {
                        Map<String, Object> resultObj = (Map<String, Object>) payloadMsg.get("result");
                        if (resultObj.containsKey("text")) {
                            return (String) resultObj.get("text");
                        }
                    }
                }
            }
            
            return result;
        } catch (Exception e) {
            System.err.println("解析ASR响应失败: " + e.getMessage());
            return "";
        }
    }

    private byte[] upzip(byte[] payload) {
        try {
            // 1. 创建字节数组输入流，将待解压的数据包装成输入流
            ByteArrayInputStream bais = new ByteArrayInputStream(payload);

            // 2. 创建GZIP输入流，用于解压GZIP格式的数据
            GZIPInputStream gzip = new GZIPInputStream(bais);

            // 3. 创建字节数组输出流，用于存储解压后的数据
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            // 4. 创建缓冲区，用于临时存储读取的数据
            byte[] buffer = new byte[1024];
            int len;

            // 5. 循环读取解压后的数据，直到全部读完
            while ((len = gzip.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }

            // 6. 关闭流资源
            gzip.close();
            baos.close();

            // 7. 返回解压后的字节数组
            return baos.toByteArray();
        } catch (IOException e) {
            // 8. 如果解压过程中出现IO异常，抛出运行时异常
            throw new RuntimeException(e);
        }
    }


    /**
     * 连接七牛云ASR服务
     */
    public void connectToQiniuAsr(Consumer<String> callback) {
        this.resultCallback = callback;
        resultFuture = new CompletableFuture<>();

        try {
            URI serverUri = new URI(QINIU_ASR_URL);
            Map<String, String> headers = new HashMap<>();
            headers.put("Authorization", "Bearer " + OPENAI_KEY);
            
            wsClient = new WebSocketClient(serverUri, headers) {
                @Override
                public void onOpen(ServerHandshake handshake) {
                    System.out.println("连接到七牛云ASR服务成功");
                    try {
                        sendConfig();
                    } catch (Exception e) {
                        System.err.println("发送配置失败: " + e.getMessage());
                        resultFuture.completeExceptionally(e);
                    }
                }
                
                @Override
                public void onMessage(String message) {
                    // 文本消息不直接打印日志，必要时可用于调试
                }
                
                @Override
                public void onMessage(ByteBuffer bytes) {
                    String text = parseTextFromResponse(bytes);
                    if (!text.isEmpty()) {
                        if (resultCallback != null) {
                            try {
                                resultCallback.accept(text);
                            } catch (Exception e) {
                                System.err.println("推送识别结果失败: " + e.getMessage());
                            }
                        }
                    }
                }
                
                @Override
                public void onClose(int code, String reason, boolean remote) {
                    System.out.println("ASR连接关闭: " + reason);
                }
                
                @Override
                public void onError(Exception ex) {
                    System.err.println("ASR连接错误: " + ex.getMessage());
                }
            };
            
            wsClient.connect();
            
        } catch (Exception e) {
            System.err.println("连接ASR服务失败: " + e.getMessage());
        }
    }
    
    /**
     * 关闭连接
     */
    public void closeConnection() {
        if (wsClient != null && wsClient.isOpen()) {
            wsClient.close();
        }
    }
}